/***************************************************************************
 *   Copyright (C) 2005 by Dominic Rath                                    *
 *   Dominic.Rath@gmx.de                                                   *
 *                                                                         *
 *   Copyright (C) 2007,2008 Øyvind Harboe                                 *
 *   oyvind.harboe@zylin.com                                               *
 *                                                                         *
 *   Copyright (C) 2009 SoftPLC Corporation                                *
 *       http://softplc.com                                                *
 *   dick@softplc.com                                                      *
 *                                                                         *
 *   Copyright (C) 2009 Zachary T Welch                                    *
 *   zw@superlucidity.net                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

#include <stdlib.h>
#include <string.h>

#include <jtag/commands.h>

enum scan_type jtag_scan_type(const struct scan_command *cmd)
{
	int i;
	int type = 0;

	for (i = 0; i < cmd->num_fields; i++)
	{
		if (cmd->fields[i].in_value)
			type |= SCAN_IN;
		if (cmd->fields[i].out_value)
			type |= SCAN_OUT;
	}

	return (enum scan_type)type;
}

int jtag_scan_size(const struct scan_command *cmd)
{
	int bit_count = 0;
	int i;

	/* count bits in scan command */
	for (i = 0; i < cmd->num_fields; i++)
		bit_count += cmd->fields[i].num_bits;

	return bit_count;
}

int jtag_build_buffer(const struct scan_command *cmd, uint8_t **buffer)
{
	int bit_count = 0;
	int i;

	bit_count = jtag_scan_size(cmd);
	*buffer = (uint8_t *)calloc(1, DIV_ROUND_UP(bit_count, 8));
	if (!(*buffer))
		return -1;

	bit_count = 0;

	LOG_DEBUG_IO("%s num_fields: %i",
				 cmd->ir_scan ? "IRSCAN" : "DRSCAN",
				 cmd->num_fields);

	for (i = 0; i < cmd->num_fields; i++)
	{
		if (cmd->fields[i].out_value)
		{

			buf_set_buf(cmd->fields[i].out_value, 0, *buffer,
						bit_count, cmd->fields[i].num_bits);
		}
		else
		{
			LOG_DEBUG_IO("fields[%i].out_value[%i]: NULL",
						 i, cmd->fields[i].num_bits);
		}

		bit_count += cmd->fields[i].num_bits;
	}

	/*LOG_DEBUG_IO("bit_count totalling: %i",  bit_count); */

	return bit_count;
}

int jtag_read_buffer(uint8_t *buffer, const struct scan_command *cmd)
{
	int i;
	int bit_count = 0;
	int retval;

	/* we return ERROR_OK, unless a check fails, or a handler reports a problem */
	retval = ERROR_OK;

	for (i = 0; i < cmd->num_fields; i++)
	{
		/* if neither in_value nor in_handler
		 * are specified we don't have to examine this field
		 */
		if (cmd->fields[i].in_value)
		{
			int num_bits = cmd->fields[i].num_bits;
			uint8_t *captured = (uint8_t *)buf_set_buf(buffer, bit_count,
													   malloc(DIV_ROUND_UP(num_bits, 8)), 0, num_bits);

			if (cmd->fields[i].in_value)
				buf_cpy(captured, cmd->fields[i].in_value, num_bits);

			free(captured);
		}
		bit_count += cmd->fields[i].num_bits;
	}

	return retval;
}

/*****************************************************************/
struct cmd_queue_page
{
	struct list_head node;
};

jtag_cmds::jtag_cmds()
{
	INIT_LIST_HEAD(&_cmds);
	INIT_LIST_HEAD(&_mem);
}

/**
 * Copy a struct scan_field for insertion into the queue.
 *
 * This allocates a new copy of out_value using cmd_queue_alloc.
 */
void jtag_cmds::jtag_scan_field_clone(struct scan_field *dst, const struct scan_field *src)
{
	dst->num_bits = src->num_bits;
	dst->out_value = (uint8_t *)buf_cpy(src->out_value, cmd_queue_alloc(DIV_ROUND_UP(src->num_bits, 8)), src->num_bits);
	dst->in_value = src->in_value;
}

void jtag_cmds::jtag_queue_command(struct jtag_command *cmd)
{
	INIT_LIST_HEAD(&cmd->node);
	list_add_tail(&cmd->node, &_cmds);
}

struct list_head *jtag_cmds::queue_head()
{
	return &_cmds;
}

void *jtag_cmds::cmd_queue_alloc(size_t size)
{
	struct cmd_queue_page *p;
	void *ret = 0;

	p = (struct cmd_queue_page *)malloc(sizeof(*p) + size);
	if (p)
	{
		memset(p, 0, sizeof(*p) + size);
		INIT_LIST_HEAD(&p->node);
		list_add_tail(&p->node, &_mem);

		ret = (char *)p + sizeof(*p);
	}

	return ret;
}

void jtag_cmds::cmd_queue_free()
{
	struct cmd_queue_page *p, *n;

	INIT_LIST_HEAD(&_cmds);

	__list_for_each_entry_safe(p, n, &_mem, node, struct cmd_queue_page)
	{
		list_del(&p->node);
		free(p);
	}

	INIT_LIST_HEAD(&_mem);
}
